﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Runtime.Serialization;

namespace MonoStrategy
{
    /// <summary>
    /// To prevent System.Drawing dependency just for this structure.
    /// </summary>
    [Serializable, DataContract]
    public struct Rectangle
    {
        [XmlAttribute, DataMember]
        public int X;
        [XmlAttribute, DataMember]
        public int Y;
        [XmlAttribute, DataMember]
        public int Width;
        [XmlAttribute, DataMember]
        public int Height;

        public int Top { get { return Y; } }
        public int Left { get { return X; } }
        public int Right { get { return X + Width; } }
        public int Bottom { get { return Y + Height; } }

        public void Extend(Point inToContainPoint)
        {
            X = Math.Min(X, inToContainPoint.X);
            Y = Math.Min(Y, inToContainPoint.Y);
            Width = Math.Max(Width, inToContainPoint.X - X);
            Height = Math.Max(Height, inToContainPoint.Y - Y);
        }

        public bool Contains(Point inPoint)
        {
            return ((Left <= inPoint.X) && (Right >= inPoint.X) && (Top <= inPoint.Y) && (Bottom >= inPoint.Y)) ;
        }

        public Rectangle(int inX, int inY, int inWidth, int inHeight)
        {
            X = inX;
            Y = inY;
            Width = inWidth;
            Height = inHeight;
        }

        public bool CanEnclose(Rectangle inRegion)
        {
            return (inRegion.Width <= Width) && (inRegion.Height <= Height);
        }

        public static bool operator !=(Rectangle inA, Rectangle inB)
        {
            return (inA.X != inB.X) || (inA.Y != inB.Y) || (inA.Width != inB.Width) || (inA.Height != inB.Height);
        }

        public static bool operator ==(Rectangle inA, Rectangle inB)
        {
            return (inA.X == inB.X) && (inA.Y == inB.Y) && (inA.Width == inB.Width) && (inA.Height == inB.Height);
        }

        public override bool Equals(object obj)
        {
            if (obj is Rectangle)
                return (Rectangle)obj == this;
            else
                return false;
        }

        public override string ToString()
        {
            return String.Format("X:{0}, Y:{1}, W:{2}, H:{3}", X, Y, Width, Height);
        }
    }

    /// <summary>
    /// To prevent System.Drawing dependency just for this structure.
    /// </summary>
    [Serializable, DataContract]
    public struct Point
    {
        [XmlAttribute, DataMember]
        public int X;
        [XmlAttribute, DataMember]
        public int Y;

        public Point(int inX, int inY)
        {
            X = inX;
            Y = inY;
        }


        public static bool operator !=(Point inA, Point inB)
        {
            return (inA.X != inB.X) || (inA.Y != inB.Y);
        }

        public static bool operator ==(Point inA, Point inB)
        {
            return (inA.X == inB.X) && (inA.Y == inB.Y);
        }

        public override bool Equals(object obj)
        {
            if (obj is Point)
                return (Point)obj == this;
            else
                return false;
        }

        public double DistanceTo(Point inPoint)
        {
            return Math.Sqrt((inPoint.X - X) * (inPoint.X - X) + (inPoint.Y - Y) * (inPoint.Y - Y));
        }

        public override string ToString()
        {
            return "X: " + X + "; Y: " + Y;
        }
    }

    [Serializable]
    public struct PointDouble
    {
        public Double X;
        public Double Y;

        public PointDouble(double inX, double inY)
        {
            X = inX;
            Y = inY;
        }

        public override string ToString()
        {
            return String.Format("X:{0:0.##}, Y:{1:0.##}", X, Y);
        }

        public static bool operator !=(PointDouble inA, PointDouble inB)
        {
            return (inA.X != inB.X) || (inA.Y != inB.Y);
        }

        public static bool operator ==(PointDouble inA, PointDouble inB)
        {
            return (inA.X == inB.X) && (inA.Y == inB.Y);
        }

        public double DistanceTo(Point inPoint)
        {
            return Math.Sqrt((inPoint.X - X) * (inPoint.X - X) + (inPoint.Y - Y) * (inPoint.Y - Y));
        }

        public override bool Equals(object obj)
        {
            if (obj is PointDouble)
                return (PointDouble)obj == this;
            else
                return false;
        }
    }

    [Serializable]
    public struct RectangleDouble
    {
        public Double X;
        public Double Y;
        public Double Width;
        public Double Height;

        public Double Top { get { return Y; } }
        public Double Left { get { return X; } }
        public Double Right { get { return X + Width; } }
        public Double Bottom { get { return Y + Height; } }

        public RectangleDouble(double inX, double inY, double inWidth, double inHeight)
        {
            X = inX;
            Y = inY;
            Width = inWidth;
            Height = inHeight;
        }

        public override string ToString()
        {
            return String.Format("X:{0:0.##}, Y:{1:0.##}, W:{2:0.##}, H:{3:0.##}", X, Y, Width, Height);
        }
    }

    /// <summary>
    /// A cycle point is a key element in deterministic, discretized space-time. Even if it provides
    /// double values for rendering, it internally uses exact integers to manage its "solid" state.
    /// </summary>
    public struct CyclePoint
    {
        /// <summary>
        /// The only deterministic way to change a cycle point, by raw cycle manipulation.
        /// </summary>
        internal Int64 XCycles;
        /// <summary>
        /// The only deterministic way to change a cycle point, by raw cycle manipulation.
        /// </summary>
        internal Int64 YCycles;

        public const Double CYCLE_MILLIS = 33;

        public static bool operator !=(CyclePoint inA, CyclePoint inB)
        {
            return (inA.XCycles != inB.XCycles) || (inA.YCycles != inB.YCycles);
        }

        public static bool operator ==(CyclePoint inA, CyclePoint inB)
        {
            return (inA.XCycles == inB.XCycles) && (inA.YCycles == inB.YCycles);
        }

        public override bool Equals(object obj)
        {
            if (obj is CyclePoint)
                return (CyclePoint)obj == this;
            else
                return false;
        }

        /// <summary>
        /// Returns the floored X offset in terms of grid cells.
        /// </summary>
        public Int32 XGrid
        {
            get { return (Int32)Math.Floor(XCycles / CYCLE_MILLIS); }
        }

        /// <summary>
        /// Returns the floored Y offset in terms of grid cells.
        /// </summary>
        public Int32 YGrid
        {
            get { return (Int32)Math.Floor(YCycles / CYCLE_MILLIS); }
        }

        /// <summary>
        /// Returns the double precision Y offset in terms of grid cells.
        /// </summary>
        public Double Y
        {
            get { return YCycles / CYCLE_MILLIS; }
        }

        /// <summary>
        /// Returns the double precision X offset in terms of grid cells.
        /// </summary>
        public Double X
        {
            get { return XCycles / CYCLE_MILLIS; }
        }

        /// <summary>
        /// Converts a grid position into a cycle point.
        /// </summary>
        public static CyclePoint FromGrid(Point inPoint)
        {
            return FromGrid(inPoint.X, inPoint.Y);
        }

        /// <summary>
        /// Converts a grid position into a cycle point.
        /// </summary>
        public static CyclePoint FromGrid(Double inX, Double inY)
        {
            CyclePoint result = new CyclePoint();

            result.XCycles = (Int64)Math.Ceiling(inX * CYCLE_MILLIS);
            result.YCycles = (Int64)Math.Ceiling(inY * CYCLE_MILLIS);

            return result;
        }

        /// <summary>
        /// Converts a cycle point into an integer grid position.
        /// </summary>
        public Point ToPoint()
        {
            return new Point(XGrid, YGrid);
        }

        /// <summary>
        /// Initializes a cycle point structure with raw cycle offsets. You should
        /// only use this when you know what you are doing ;).
        /// </summary>
        public CyclePoint(Int64 inXCycles, Int64 inYCycles)
        {
            XCycles = inXCycles;
            YCycles = inYCycles;
        }

        /// <summary>
        /// The only deterministic way to change a cycle point, by raw cycle manipulation.
        /// </summary>
        public CyclePoint AddCycleVector(Point inCycleVector)
        {
            return new CyclePoint(XCycles + inCycleVector.X, YCycles + inCycleVector.Y);
        }

        public override string ToString()
        {
            return String.Format("XCycles: {0} ({2:0.##}); YCycles: {1} ({3:0.##})", XCycles, YCycles, X, Y);
        }
    }
}
